| Spell_Routines.inc v3.0
| Originally Written by Rusty~
| Upkeep performed by A_Druid_00
| Updated Slighty by Zergling of PEQ, Live for the Swarm!
| Includes MQ2Exchange integration for item swapping by BrainDeath
| Also includes FD fix that would cause an endless loop if you were hit by FD mid-cast by A_Druid_00
| Features:
| - Casts spells, clicks items, or uses AA abilities for you
| - Allows back to back casting without waiting on spell gems to pop all the way up
| - Will interrupt spell if target dies while casting. If on a mount, it will dismount and duck if the time left
|   is greater than 7 seconds, else it will move forward a bit to interrupt, then move you back
|    ** IMPORTANT: if you don't want to interrupt a spell while mounted, put this at the top of your macro: **
|    **   /declare noInterrupt int outer 1                                                                  **
| - Allows you to use items in bags. Equips item, clicks it, then returns it to its previous location
| - Lets you set how long you want to keep trying to cast the spell (defaults to 0)
|   If the spell is interrupted before the given time, it will recast, else it will return CAST_INTERRUPTED
| - Lets you call a custom subroutine while waiting for spell to finish casting
|   Try to keep custom subroutines very small. A common use would be to interrupt the spell if a certain condition is true
| - This file also includes a sub named Interrupt. You can call this to interrupt any spell you're casting instantly.
| - You can also use the SwapItem sub included in this to swap items to certain slots
| - Added EquipItem sub to easily equip items in your main Inventory slots.
| - Note: if you don't want this to cast spells while you're invis, in your main macro have this at the top:
|      /declare noInvis int outer 1
|   This will make it return CAST_INVIS if you're invis
|  Below is a list of outer scope variables you can access in your macros:
|      refreshTime        - How much time is left till you're done recovering from casting
|      castEndTime        - How much time left till you're done casting the current spell... usable in custom spell Subs
|      spellNotHold       - 1 if your last spell didn't take hold, 0 otherwise
|      spellRecastTime1-9 - How much time left till that spell is back up
| ======================================================================================================================
|  EquipItem:  An easier way to equip items you have in bags ( useful for weapons or focus items )
|              slot name is optional. If not given, it will equip it in the first possible spot
|    Usage:
|        /call EquipItem "item name|slotname"
|        Returns: "old item name|slotname"
|    Examples:
|    To Equip Sharp Ended Broken Lever when you have Serpent of Vindication equiped:
|        /call EquipItem "Sharp Ended Broken Lever"
|    It will return "Staff of Vindication|mainhand"
|    To reequip the original item, you can save the return in a variable, and then use it later like this:
|       /varset oldPrimary ${Macro.Return}
|       | ... do stuff here with your new item equiped
|       /call EquipItem ${oldPrimary}
| ======================================================================================================================
|  SwapItem:  a subroutine which is used in the Cast sub itself. You don't need to do this to cast an item in a bag
|             but you can call it in your macro to SwapItems (such as weapons or focus items)
|    Usage:
|        /call SwapItem "item name" slotname
|    Examples:
|    To swap Darkblade of the Warlord to your main hand:
|        /call SwapItem "Darkblade of the Warlord" mainhand
|    To swap stat food in one bag with other food in another bag:
|        /call SwapItem "Bristlebanes Party Platter" ${FindItem[halas 10lb meat pie].InvSlot}
| ======================================================================================================================
|  Cast: the main subroutine that casts spells or items for you
|     Usage:
|        /call Cast "spellname|itemname|AAname|AA#" [item|alt|gem#] [give up time][s|m] [custom subroutine name] [Number of resist recasts]
|     Examples:
|     To cast Howl of Tashan and mem it in slot 3 if not memmed:
|       /call Cast "Howl of Tashan" gem3
|     To cast Arcane Rune and keep trying for 7 seconds, in case of interrupts.
|       /call Cast "Arcane Rune" gem5 7s
|     To click Grim Aura earring that's in a bag:
|       /call Cast "Shrunken Goblin Skull Earring" item
|     To use AA ability Eldritch Rune:
|       /call Cast "Eldritch Rune" alt
|         or
|       /call Cast "173" alt
|     To call a subroutine that interrupts CH if target gets healed before it lands:
|       /call Cast "Complete Healing" gem1 0 CheckHP
|     Then in your macro have somewhere:
|       Sub CheckHP
|          /if ( ${Target.PctHPs}>=80 ) /call Interrupt
|       /return
| Returns these values:
| ----------------------+----------------------------------------------------------------------+
| CAST_CANCELLED       |   Casting was aborted
| CAST_COLLAPSE       |   Your Gate collapsed
| CAST_DISTRACTED       |   You were distracted
| CAST_FIZZLE         |   Your cast fizzled
| CAST_INTERRUPTED       |   Casting was interupted
| CAST_INVISIBLE       |   You are invisible
| CAST_NOTMEMMED        |    Spell is not memmed and you gem to mem was not specified
| CAST_NOTARGET         |   No target
| CAST_NOTREADY       |   Not ready to cast
| CAST_OUTOFMANA       |   Not enough mana to cast spell
| CAST_OUTOFRANGE      |   Target is out of range
| CAST_OUTDOORS         |   Spell not working here (on mount ect..)
| CAST_RECOVER         |   Spell is not ready
| CAST_RESIST         |   Cast was resisted
| CAST_STANDING         |   Not standing
| CAST_STUNNED         |   You are stunned
| CAST_SUCCESS         |   The cast was a success
| CAST_TAKEHOLD         |   The spell did not take hold
| CAST_CANNOTSEE       |   Cannot see target
| CAST_COMPONENTS       |   Missing Component
| CAST_UNKNOWNSPELL     |    Spell/Item/Ability was not found
----------------------+----------------------------------------------------------------------+
#Event BeginCast    "You begin casting#*#"
#Event BeginCast   "You are already on a mount#*#"
#Event Collapse    "Your gate is too unstable, and collapses.#*#"
#Event FDFail       "#1# has fallen to the ground.#*#"
#Event Components   "You are missing some required components#*#"
#Event Components   "You need to play a#*#instrument for this song#*#"
#Event Distracted   "You are too distracted to cast a spell now#*#"
#Event Distracted   "You can't cast spells while invulnerable#*#"
#Event Distracted   "You *CANNOT* cast spells you have been silenced#*#"
#Event Immune      "Your target has no mana to affect#*#"
#Event Immune      "Your target is immune to changes in its attack speed#*#"
#Event Immune      "Your target is immune to changes in its run speed#*#"
#Event Immune      "Your target cannot be mesmerized#*#"
#Event Immune      "Your target looks unaffected#*#"
#Event Interrupt   "Your spell is interrupted#*#"
#Event Interrupt   "Your casting has been interrupted#*#"
#Event NoLOS       "You cannot see your target.#*#"
#Event Fizzle      "Your spell fizzles#*#"
#Event Fizzle      "You miss a note bringing your song to a close#*#"
#Event NoTarget      "You must first select a target for this spell#*#"
#Event NoTarget      "This spell only works on#*#"
#Event NoTarget      "You must first target a group member#*#"
#Event NotReady      "Spell recast time not yet met#*#"
#Event OutOfMana   "Insufficient Mana to cast this spell#*#"
#Event OutOfRange   "Your target is out of range get closer#*#"
#Event Outdoors      "This spell does not work here#*#"
#Event Outdoors      "You can only cast this spell in the outdoors#*#"
#Event Outdoors      "You can not summon a mount here#*#"
#Event Outdoors      "You must have both the Horse Models and your current Luclin Character Model enabled to summon a mount#*#"
#Event Recover      "You haven't recovered yet#*#"
#Event Recover      "Spell recovery time not yet met#*#"
#Event Resist      "Your target resisted the#*#spell#*#"
#Event Resisted2    "You resist the #1# spell#*#"
#Event Standing      "You must be standing to cast a spell#*#"
#Event Stunned      "You can't cast spells while stunned#*#"
#Event Takehold      "Your spell did not take hold#*#"
#Event Takehold      "Your spell would not have taken hold#*#"
#Event Takehold      "Your spell is too powerfull for your intended target#*#"
#Event Takehold      "You need to be in a more open area to summon a mount#*#"
#Event Takehold      "You can only summon a mount on dry land#*#"
#Event Takehold      "This pet may not be made invisible#*#"

Sub Cast(spellName,spellType,giveUpValue,mySub,int ResistTotal)
/declare castTime float local
/if (!${Defined[castReturn]}) /declare castReturn string outer CAST_CANCELLED
   /if (${Me.Invis} && ${noInvis}) /return
   /if (${spellType.Equal[item]}) {
      /if (!${FindItem[${spellName}].InvSlot}) /return CAST_UNKNOWNSPELL
      /varset castTime ${FindItem[${spellName}].CastTime}
   } else /if (${spellType.Equal[alt]}) {
      /if (!${Me.AltAbilityReady[${spellName}]}) /return CAST_NOTREADY
      /varset castTime ${Me.AltAbility[${spellName}].Spell.MyCastTime}
   } else {
      /if (!${Me.Book[${spellName}]}) /return CAST_UNKNOWNSPELL
      /declare spellID int local ${Me.Book[${Me.Book[${spellName}]}].ID}
      /varset castTime ${Spell[${spellName}].MyCastTime}
      /if (${Me.CurrentMana}<${Spell[${spellID}].Mana}) /return CAST_OUTOFMANA
   }
   /if (${castTime}>0.1) {
      /if (${NetAdvPath.Following} && !${NetAdvPath.Paused}) /squelch /netfollow pause
      /if (${Stick.Status.Equal[ON]}) /stick pause
      /if (${FollowFlag}) /call PauseFunction
      /if (${Me.Moving}) /keypress back
   }
   /if (!${Defined[spellType]}) /declare spellType string local spell
   /if (!${Defined[spellRecastTime1]}) {
      /if (!${Defined[noInterrupt]}) /declare noInterrupt int outer 0
      /declare ResistCounter int outer
      /declare moveBack bool outer false
      /declare selfResist int outer
      /declare selfResistSpell string outer
      /declare giveUpTimer timer outer
      /declare castEndTime timer outer
      /declare refreshTime timer outer
      /declare itemRefreshTime float outer
      /declare i int local
      /declare spellNotHold int outer
      /delay 5
      /for i 1 to 9
      /declare spellRecastTime${i} timer outer
      /if (${Me.SpellReady[${i}]}) {
         /varset spellRecastTime${i} 0
      } else {
         /varcalc spellRecastTime${i} 10*${Me.Gem[${i}].RecastTime}
      }
      /next i
   }
   /if (${Defined[giveUpValue]}) /varset giveUpTimer ${giveUpValue}
   /if (${Defined[ResistTotal]}) /varset ResistCounter ${ResistTotal}
   /varset spellNotHold 0
   /varset selfResist 0
   :wait_for_stop
   /if (${Me.Casting.ID} || (${Me.Moving} && ${castTime}>0.1)) {
      /if (${Bool[${mySub}]}) /call ${mySub} ${spellID}
      /goto :wait_for_stop
   }
   /if (${Window[SpellBookWnd].Open}) /keypress spellbook
   /if (${Me.Ducking}) /keypress duck
   /call DoCastingEvents
   /varset castReturn X
   /if (${spellType.Equal[item]}) /call ItemCast "${spellName}" "${mySub}"
   /if (${spellType.Equal[alt]}) /call AltCast "${spellName}" "${mySub}"
   /if (${spellType.NotEqual[item]} && ${spellType.NotEqual[alt]}) /call SpellCast "${spellType}" "${spellName}" "${mySub}" "${spellID}" "${giveUpValue}"
   /if (${Stick.Status.Equal[PAUSED]}) /squelch /stick unpause
   /if (${NetAdvPath.Paused}) /squelch /netfollow unpause
   /if (${PauseFlag}) /call PauseFunction
   /varset giveUpTimer 0
   /varset ResistCounter 0
/return ${castReturn}

Sub SpellCast(spellType,spellName,mySub,int spellID,giveUpValue)
:cast_spell
/if (!${Me.Gem[${spellName}]}) {
   /if (${Cursor.ID}) /call ClearCursor
   /if (${spellType.Left[3].Equal[gem]}) {
      /memspell ${spellType.Right[1]} "${spellName}"
   } else {
      /return CAST_NOTMEMMED
   }
   /if (${Bool[${mySub}]}) /call ${mySub} ${spellID}
   /delay 6s ${Me.Gem[${spellName}]}
   /if (${Me.Gem[${spellName}]}) /varcalc spellRecastTime${Me.Gem[${spellName}]} 10*${Spell[${spellID}].RecastTime}
   /if (!${Me.Gem[${spellName}]}) /return CAST_INTERRUPTED
   :wait_for_mem
   /if (${Bool[${mySub}]}) /call ${mySub} ${spellID}
   /delay 15s ${Me.SpellReady[${spellName}]}
   /if (!${Me.SpellReady[${spellName}]}) {
      /if (${giveUpTimer}) /goto :wait_for_mem
      /return CAST_NOTREADY
   }
}
   /varset spellType spell
   /if (${spellName.Find[illusion: ]} && ${Me.AltAbilityReady[project illusion]}) /call Cast "project illusion" alt
   /varset giveUpTimer ${giveUpValue}
   /declare recoverWaitTime timer local 30
   :cast_spell_loop
   /if (!${Me.SpellReady[${spellName}]} && (${spellRecastTime${Me.Gem[${spellName}]}}<${giveUpTimer} || ${refreshTime}>0 || ${castReturn.Equal[CAST_RESISTED]})) {
      /if (${Bool[${mySub}]}) /call ${mySub} ${spellID}
      /goto :cast_spell_loop
   } else {
      /if (!${Me.SpellReady[${spellName}]} && !${castReturn.Equal[CAST_RESISTED]}) /return CAST_NOTREADY
   }
   /cast "${spellName}"
   /if (${Me.Casting.ID}) {
      /varset spellID ${Me.Casting.ID}
      /varcalc castEndTime ${Me.Casting.MyCastTime}*10
      /if (${castEndTime}<${Math.Calc[${Me.Casting.CastTime}*5]}) /varcalc castEndTime ${Me.Casting.CastTime}*5
   }
   /varset moveBack false
   /call WaitCast ${mySub} ${spellID}
   /if (${moveBack}) {
      /keypress back hold
      /delay 4
      /keypress back
      /delay 15 !${Me.Moving}
   }
   /if (${castReturn.Equal[CAST_CANCELLED]}) /return CAST_CANCELLED
   /call DoCastingEvents
   /if (!${castReturn.Equal[CAST_SUCCESS]}) {
      /if (${castReturn.Equal[CAST_RECOVER]}) {
         /if (!${recoverWaitTime}) {
            /varcalc spellRecastTime${Me.Gem[${spellName}]} 10*${Spell[${spellID}].RecastTime}
            /if (!${giveUpTimer}) /return CAST_NOTREADY
         }
         /goto :cast_spell_loop
      }
      /if (${castReturn.Equal[CAST_RESTART]} || ${castReturn.Equal[CAST_STUNNED]} || ${castReturn.Equal[CAST_FIZZLE]} || ${castReturn.Equal[CAST_COLLAPSE]} || (${castReturn.Equal[CAST_INTERRUPTED]} && ${giveUpTimer}) || (${castReturn.Equal[CAST_RESISTED]} && ${ResistCounter})) /goto :cast_spell_loop
   }
   /if (!${castReturn.Equal[CAST_CANNOTSEE]} && !${castReturn.Equal[CAST_OUTOFRANGE]} && !${castReturn.Equal[CAST_OUTOFMANA]} && !${castReturn.Equal[CAST_NOTARGET]} && !${castReturn.Equal[CAST_INTERRUPTED]}) {
      /varcalc refreshTime 10*${Spell[${spellID}].RecoveryTime}
      /varcalc spellRecastTime${Me.Gem[${spellName}]} 10*${Spell[${spellID}].RecastTime}
   }
/return

Sub ItemCast(spellName,mySub)
/declare charges int local
/declare oldItemName string local
/declare slotName string local
/declare swapItemBack bool local false
:cast_item
/if (${FindItem[${spellName}].InvSlot}>21) {
   /varset swapItemBack true
   /if (${FindItem[${spellName}].WornSlot[1]} && ${FindItem[${spellName}].EffectType.Find[worn]}) {
      /varset slotName ${FindItem[${spellName}].WornSlot[1].Name}
   } else /if (${FindItem[${spellName}].InvSlot}>29) {
      /varset slotName pack8
   } else {
      /varset slotName ${FindItem[${spellName}].InvSlot.Name}
   }
   /varset oldItemName ${InvSlot[${slotName}].Item.Name}
   /call SwapItem "${spellName}" ${slotName}
}
:wait_item_loop
   /if (${itemRefreshTime}>${MacroQuest.Running}) /goto :wait_item_loop
   /varset itemRefreshTime ${Math.Calc[${MacroQuest.Running}+000]}
   /varset charges ${FindItem[${spellName}].Charges}
   /cast item "${spellName}"
   /if (${Me.Casting.ID}) /varcalc castEndTime ${FindItem[${spellName}].CastTime}*10
   /if (${charges}) /delay 1s ${FindItem[${spellName}].Charges}!=${charges}
   /call WaitCast ${mySub}
   /if (${swapItemBack} && ${FindItem[${oldItemName}].ID}) /call SwapItem "${oldItemName}" ${slotName}
   /if (${castReturn.Equal[CAST_CANCELLED]}) /return CAST_CANCELLED
   /call DoCastingEvents
   /if (${castReturn.Equal[CAST_RESTART]} || ${castReturn.Equal[CAST_STUNNED]} || (${castReturn.Equal[CAST_INTERRUPTED]} && ${giveUpTimer}) || ${castReturn.Equal[CAST_COLLAPSE]} || (${castReturn.Equal[CAST_RESISTED]} && ${ResistCounter})) /goto :cast_item
/return

Sub AltCast(spellName,mySub)
:cast_alt
/alt activate ${Me.AltAbility[${spellName}].ID}
/if (${Me.AltAbility[${spellName}].Spell.MyCastTime}>=0.5) /delay 1s ${Me.Casting.ID}
/call WaitCast ${mySub}
/if (${castReturn.Equal[CAST_CANCELLED]}) /return CAST_CANCELLED
/call DoCastingEvents
/if (${castReturn.Equal[CAST_RESTART]} || ${castReturn.Equal[CAST_STUNNED]} || (${castReturn.Equal[CAST_INTERRUPTED]} && ${giveUpTimer}) || (${castReturn.Equal[CAST_RESISTED]} && ${ResistCounter})) /goto :cast_alt
/return

Sub ClearCursor
/declare i int local
:auto_inv
/if (${Cursor.ID}) {
   /if (${Cursor.Container}) {
      /for i 1 to 8
      /if (!${InvSlot[pack${i}].Item.Container}) /nomodkey /itemnotify pack${i} leftmouseup
      /next i
   } else {
      /autoinventory
   }
   /goto :auto_inv
}
/return

Sub DoCastingEvents
/doevents Recover
/doevents BeginCast
/doevents Fizzle
/doevents Interrupt
/doevents Standing
/doevents FDFail
/doevents OutOfRange
/doevents OutOfMana
/doevents NoLOS
/doevents Resisted2
/doevents Resisted
/doevents Immune
/doevents Stunned
/doevents Collapse
/doevents NoTarget
/doevents NotReady
/doevents NoHold
/doevents Components
/doevents Distracted
/doevents Outdoors
/return

Sub EquipItem(WhatWhere)
/declare DestName string local
/declare ItemName string local ${WhatWhere.Arg[1,|]}
/declare SlotName string local ${WhatWhere.Arg[2,|]}
/if (${SlotName.Equal[NULL]}) /varset SlotName ${InvSlot[${FindItem[=${ItemName}].WornSlot[1]}].Name}
/if (${FindItem[=${ItemName}].InvSlot}<22 || !${FindItem[=${ItemName}].WornSlot[${SlotName}]}) /return
/if (!${InvSlot[${SlotName}].Item.Name.Equal[NULL]}) /varset DestName "${InvSlot[${SlotName}].Item.Name}|${SlotName}"
/call SwapItem "${ItemName}" "${SlotName}"
/return ${DestName}

Sub Interrupt
/if (${Me.Mount.ID}) /dismount
/stopcast
/if (${Defined[castReturn]}) /varset castReturn CAST_CANCELLED
/return ${castReturn}

Sub SwapItem(itemName,slotName)
/declare i int local
/if (${Cursor.ID}) /call ClearCursor
/exchange "${itemName}" ${slotName}
/delay 5s ${InvSlot[${slotName}].Item.Name.Equal[${itemName}]}
/if (${Cursor.ID}) /call ClearCursor
/return

Sub WaitCast(mySub,int spellID)
/declare currentTarget int local ${Target.ID}
/declare currentTargetType string local ${Target.Type}
:wait_cast_loop
/if (${Bool[${mySub}]}) /call ${mySub} ${spellID}
/if (${Me.Casting.ID}) {
  /if (${currentTarget} && !${Spawn[${currentTarget}].Type.Equal[${currentTargetType}]}) {
    /if (!${Me.Casting.TargetType.Equal[PB AE]} && !${Me.Casting.TargetType.Equal[self]} && !${moveBack} && (!${Me.Mount.ID} || !${noInterrupt})) {
      /if (!${Me.Mount.ID} || ${castEndTime}>70) {
        /call Interrupt
      } else /if (${Me.Casting.RecastTime}>3) {
        /varset castReturn CAST_CANCELLED
        /keypress forward hold
        /delay 6
        /keypress forward
        /varset moveBack true
      }
    }
  }
  /if (${Me.State.Equal[DUCK]}) /varset castReturn CAST_CANCELLED
  /goto :wait_cast_loop
}
/return

Sub Event_BeginCast
/if (${Defined[castReturn]}) /varset castReturn CAST_SUCCESS
/return

Sub Event_Collapse
/if (${Defined[castReturn]}) /varset castReturn CAST_COLLAPSE
/varset giveUpTimer 200
/return

Sub Event_Fizzle
/if (${Defined[castReturn]}) /varset castReturn CAST_FIZZLE
/return

Sub Event_Immune
/if (${Defined[castReturn]}) /varset castReturn CAST_IMMUNE
/return

Sub Event_Interrupted
/if (${Defined[castReturn]}) /varset castReturn CAST_INTERRUPTED
/return

Sub Event_TakeHold
/if (${Defined[spellNotHold]}) /varset spellNotHold 1
/return

Sub Event_NoLOS
/if (${Defined[castReturn]}) /varset castReturn CAST_CANNOTSEE
/return

Sub Event_NoTarget
/if (${Defined[castReturn]}) /varset castReturn CAST_NOTARGET
/return

Sub Event_NotReady
/if (${Defined[castReturn]}) /varset castReturn CAST_NOTREADY
/return

Sub Event_OutOfMana
/if (${Defined[castReturn]}) /varset castReturn CAST_OUTOFMANA
/return

Sub Event_OutOfRange
/if (${Defined[castReturn]}) /varset castReturn CAST_OUTOFRANGE
/return

Sub Event_Recover
/if (${Defined[castReturn]}) /varset castReturn CAST_RECOVER
/return

Sub Event_Components
/if (${Defined[castReturn]}) /varset castReturn CAST_COMPONENTS
/return

Sub Event_Distracted
/if (${Defined[castReturn]}) /varset castReturn CAST_DISTRACTED
/return

Sub Event_Outdoors
/if (${Defined[castReturn]}) /varset castReturn CAST_OUTDOORS
/return

Sub Event_Resisted(line,name)
/if (${selfResist} && ${name.Equal[${selfResistSpell}]}) /varset selfResist 0
/if (${ResistCounter}) /varcalc ResistCounter ${ResistCounter}-1
/if (${Defined[castReturn]}) /varset castReturn CAST_RESISTED
/return

Sub Event_Resisted2(line,name)
/if (${Defined[selfResist]}) {
   /varset selfResist 1
   /varset selfResistSpell ${name}
}
/return

Sub Event_Standing
/stand
/if (${Defined[castReturn]}) /varset castReturn CAST_RESTART
/return

Sub Event_Stunned
/if (${Me.Stunned}) {
   /delay 3s !${Me.Stunned}
} else {
   /delay 7
}
/if (${Defined[castReturn]}) /varset castReturn CAST_STUNNED
/return 

